<template>
  <div class="uploader" v-if="checkData">
    <slot></slot>
    <!-- <slot :files="files" :file-list="fileList" :started="started" > -->
    <div slot="tip" v-if="$slots.tip">
      <slot name="tip">
        <a-alert closable type="info">
          <span slot="message">
            <slot name="tip">上传组件</slot>
          </span>
        </a-alert>
      </slot>
    </div>
    <uploader-unsupport class="margin-b-8">
      <slot name="unsupport"></slot>
    </uploader-unsupport>
    <uploader-drop class="margin-b-8" v-if="drag && !uploadDisabledComputed">
      <slot name="dropText">
        <p>把文件拖入指定区域，完成上传。</p>
      </slot>

      <!-- <uploader-btn :directory="true">上传文件夹</uploader-btn> -->
    </uploader-drop>
    <div class="margin-b-8">
      <ht-upload
        class="ht-upload"
        ref="miniAttachUpload"
        :multiple="multiple"
        :accept="accept"
        :dbKey="query.dbKey"
        :busField="query.busField"
        :busKey="query.busKey"
        :disabled="disabled"
        :busType="query.busType"
        :ftpKey="query.ftpKey"
        :on-success="noop"
        :onError="noop"
        :onExceed="filesAdded"
        :beforeUpload="minBeforeUpload"
        :onPreview="onPreview"
        :limit="limit"
        :showFileList="false"
        :httpRequest="upload"
      >
      <slot name="minTrigger">
            <a-button
          class="minio-btn"
          slot="trigger"
          type="primary"
          :disabled="uploadDisabledComputed"
          v-show="!uploadDisabledComputed && uploadShow"
          ref="minUploadBtn"
        >上传</a-button>
      </slot>
      </ht-upload>
      <uploader-btn
        :uploadDisabled="uploadDisabledComputed"
        v-show="!uploadDisabledComputed && uploadShow"
        class="minio-btn"
        :single="multiple"
        :accept="accept"
        :attrs="attrs"
        ref="largeUploadBtn"
      >
        <slot name="trigger">
          <a-button   type="primary" :disabled="uploadDisabledComputed">大文件上传</a-button>
        </slot>
      </uploader-btn>
      <a-button class="minio-btn" @click="loadData" v-if="!refreshDisableComputed" >刷新</a-button>
      <a-button class="minio-btn" @click="download(0)" v-if="!downloadDisabledComputed &&  showFileList" :disabled="downloadDisabledComputed ||  !showFileList">下载</a-button>
      <a-button class="minio-btn" @click="download(1)" v-if="!downloadDisabledComputed  &&  showFileList" :disabled="downloadDisabledComputed  ||  !showFileList">一键打包下载</a-button>
      <a-button class="minio-btn" @click="deleteFile" v-if="!deleteDisabledComputed" :disabled="deleteDisabledComputed">删除</a-button>
      <slot name="btn"></slot>
    </div>
    <div v-if="!uploadDisabledComputed" class="upload-tip">文件超过50M,请使用大文件上传</div>

    <uploaded-file
      v-show="showFileList"
      class="margin-b-8"
      ref="uploadedFile"
      :previewDisabled="previewDisabledComputed"
    ></uploaded-file>
    <uploader-files class="margin-b-8"></uploader-files>
    <download-list class="margin-b-8"></download-list>

    <!-- </slot> -->
  </div>
</template>

<script>
//开发调试
// import Uploader from '../simple-uploader/dev/uploader'
//npm包
import {previewAttach } from '@/api/manage'
import '@/utils/upload.install.js'
import Uploader from 'simple-uploader.js'
import { Base64 } from 'js-base64'
import qs from 'qs'
import { kebabCase, formatSize, getExtension, isPreview, resultChange } from '../common/utils'
import minioUploaderConfig from '../common/config'
import UploaderBtn from './btn.vue'
import UploaderDrop from './drop.vue'
import UploaderUnsupport from './unsupport.vue'
import UploaderList from './list.vue'
import UploaderFiles from './files.vue'
import UploaderFile from './file.vue'
import UploadedFile from './uploadedFile.vue'
import DownloadList from './downloadList'

const COMPONENT_NAME = 'hteUpload'
const FILE_ADDED_EVENT = 'fileAdded'
const FILES_ADDED_EVENT = 'filesAdded'
const UPLOAD_START_EVENT = 'uploadStart'
import { Modal, message } from 'ant-design-vue'
import {
  uploadAttach,
  mergerFile,
  downloadAttach,
  bindBusKey,
  deleteAttach,
  getAttaches,
  getDownloadUrl,
  deleteChunks
} from '../api/upload'
function noop() {}

export default {
  name: COMPONENT_NAME,
  provide() {
    return {
      uploader: this
    }
  },
  model: {
    prop: 'fileListFinish',
    event: 'changeFile'
  },
  props: {
    options: {
      type: Object,
      default() {
        return {}
      }
    },
    attrs: {
      type: Object,
      default() {
        return { accept: '*' }
      }
    },
    accept: {
      type: String,
      default: '*'
    },
    //多文件上传
    multiple: {
      type: Boolean,
      default() {
        return true
      }
    },
    //是否拖动上传
    drag: {
      type: Boolean,
      default: false
    },
    // 数据源键
    dbKey: {
      type: String,
      required: false,
      default: ''
    },
    // ftp配置key，默认空字符串
    ftpKey: {
      type: String,
      default: ''
    },
    // 业务主键
    busKey: {
      type: String,
      required: false,
      default: ''
    },
    // 业务模块
    busField: {
      type: String,
      required: false,
      default: ''
    },
    // 业务类型
    busType: {
      type: String,
      required: false,
      default: ''
    },
    queryParams: {
      type: Object,
      // required:true,
      default() {
        return null
      }
    },

    //上传前拦截
    beforeUpload: Function,
    //删除前拦截
    beforeRemove: Function,
    //删除回调
    onRemove: {
      type: Function,
      default: noop
    },
    //文件改变事件监听
    onChange: {
      type: Function,
      default: noop
    },
    //点击文件名称
    onPreview: {
      type: Function
    },
    onSuccess: {
      type: Function,
      default: noop
    },
    onProgress: {
      type: Function,
      default: noop
    },
    onError: {
      type: Function,
      default: noop
    },
    fileListFinish: {
      type: Array,
      default() {
        return []
      }
    },
    autoUpload: {
      type: Boolean,
      default: true
    },
    //文件上传最大size默认2g
    uploadMaxSize: {
      type: Number,
      default: 2000 * 1024 * 1024
    },
    minUploadMaxSize:{
      type: Number,
      default: 50 * 1024 * 1024
    },
    //全部禁用
    disabled: {
      type: Boolean,
      default: false
    },
    uploadDisabled: {
      //上传禁用
      type: Boolean,
      default: false
    },
    uploadShow:{
      type: Boolean,
      default: true
    },
    downloadDisabled: {
      //下载禁用
      type: Boolean,
      default: false
    },
    deleteDisabled: {
      //删除禁用
      type: Boolean,
      default: false
    },
    refreshDisable:{
      //刷新禁用
      type: Boolean,
      default: false
    },
    previewDisabled: {
      //点击文件名称禁用
      type: Boolean,
      default: false
    },
    limit: Number,
    onExceed: {
      type: Function,
      default: noop
    },
    //下载并行数量默认3
    dowanloadNumber: {
      type: Number,
      default: 3
    },
    chunkSize: {
      type: Number,
      default: null
    },
    zipFileName: {
      type: String,
      default: '打包文件'
    },
    downloadMaxSize: {
      type: Number,
      default: 2000 * 1024 * 1024
    },
    saveType: {
      //下载类型blob最大2G有文件流用不了
      type: String,
      default: 'blob'
    },
    showFileList: {
      type: Boolean,
      required: false,
      default: true
    },
    oldUploadAttachDate: {
      //上传文件不存md5,就是小文件
      //兼容旧数据下载文件日期格式2021-12-09
      type: String,
      default: null
    },
  //是否开启分页
  showPage:{
     type: Boolean,
      required: false,
      default: false
  },

    // fileStatusText: {
    //   type: [Object, Function],
    //   default () {
    //     return {
    //       success: '文件合并中',
    //       errorMerge:'合并出错',
    //       error: '上传出错',
    //       uploading: '上传中',
    //       paused: '暂停中',
    //       waiting: '等待中',
    //       computeMd5:'文件校验中'
    //     }
    //   }
    // }
  },
  data() {
    return {
      query: {
        //数据源
        dbKey: '',
        // ftp配置key，默认空字符串
        ftpKey: '',
        //业务主键
        busKey: '',
        //业务模块
        busField: '',
        // 业务类型
        busType: ''
      },
      fileListDefault: this.fileListFinish || [],
      checkData: true,
      downloadTempIndex: 1,
      started: false,
      files: [],
      fileList: [],
      dowloadList: [],
      //上传文件状态显示
      fileStatusText: {
        success: '文件合并中',
        errorMerge: '合并出错',
        error: '上传出错',
        uploading: '上传中',
        paused: '暂停中',
        waiting: '等待中',
        computeMd5: '文件校验中'
      },
      finalOptions: {
        //请求头
        headers: minioUploaderConfig.headers,
        // cookie
        withCredentials: minioUploaderConfig.withCredentials,
        forceChunkSize: true,
        testChunks: false,
        method: 'multipart',
        //分片大小为10M
        chunkSize: this.chunkSize || 10 * 1024 * 1024,
        //重试次数
        maxChunkRetries: 10,
        target: minioUploaderConfig.baseURL + '/com.haday.hte.attach/attachInfo/upload',
        //上传请求参数处理

        processParams: function(params, file, chunk, isTest) {
          params.identifier = file.fileMd5
          params.size = params.totalSize
          params.fileName = params.filename
          params.fileType = file.fileType

          return params
        },
        checkChunkUploadedByResponse: function(chunk, res) {
          //返回数据回调检测文件切片是否已经上传

          let data = JSON.parse(res)
          let result = data.result

          if (data.success) {
            if (result.skipUpload) {
              return true
            }

            return (result.finishChunks || []).indexOf(chunk.offset + 1) >= 0
          }
        }
      }
    }
  },
  computed: {
    uploadDisabledComputed() {
      return this.disabled || this.uploadDisabled
    },
    downloadDisabledComputed() {
      return this.downloadDisabledHandle()
    },
    deleteDisabledComputed() {
      return this.disabled || this.deleteDisabled || !this.showFileList
    },
    previewDisabledComputed() {
      return this.disabled || this.previewDisabled 
    },
    refreshDisableComputed(){
        return this.refreshDisable || !this.showFileList 
    },
  },
  watch: {
    query: {
      deep: true,
      handler: function(value) {
        this.handleQuery(value)
      }
    },
    fileListFinish(newValue) {
      this.fileListDefault = newValue
    },
    // 双向绑定
    fileListDefault(newValue, oldValue) {
      this.$emit('changeFile', newValue)
    }
  },
  methods: {
    noop() {
      //函数挡板
    },
    upload(options) {
      let _this = this
      uploadAttach(options)
        .then(result => {
          let res = resultChange(result)
          
          if (res.success) {
            let data = res.result[0]
            res.result = data
            _this.fileListDefault.unshift(data)
            options.onSuccess(res)

            this.onSuccess(res, data, this.fileListDefault)

            this.onChange(data, this.fileListDefault)
            message.success(`${options.file.name}文件上传成功 `)
          }
        })
        .catch(result => {
          let err = resultChange(result)
          options.onError(err)
          message.error(`${options.file.name}文件上传失败 `)
          this.onError(err, options.file, this.fileListDefault)
          this.onChange(options.file, this.fileListDefault)
        })
    },
    handleQuery(value) {
      if (!value.dbKey || !value.busField || !value.ftpKey) {
        this.checkData = false
        message.error('数据源、业务模块、ftpKey必传!')
      } else {
        this.checkData = true
      }
    },
    uploadStart() {
      this.started = true
    },
    loadData() {
      let query = this.query
       if (  !!query.busKey ||  !!query.id) {
        getAttaches(this.processParams(null))
        .then(res => {
          
          this.fileListDefault = res.result || []
          this.$refs.uploadedFile.onClearSelected()
        })
        .catch(res => {})
         
      } 
      
    },
    
    download(status = 0,selectionRows = []) {
      //附件下载 selectionRows 用于refs直接调用

        if(!selectionRows || selectionRows.length==0){
            selectionRows = this.$refs.uploadedFile.selectionRows
        }
       
      if (selectionRows.length < 1 && status == 0) {
        message.warning('请选择要下载的附件')
        return
      }
      if (status == 1 && selectionRows.length < 1) {
        selectionRows = this.fileListDefault
      }
      if (status == 0 || (status == 1 && selectionRows.length==1)) {
        //单个文件不压缩打包
        selectionRows.map(item => {
          item.uid = item.uid || Date.now() + this.downloadTempIndex++
          item.zipDownload = false
          item.fileList = []
          return item
        })
        this.dowloadList.push(...selectionRows)
      } else if (status == 1) {
        if (selectionRows.length == 0) {
          message.warning('没有要下载的附件')
          return
        }

        let size = 0
        selectionRows.map(item => {
          size = size + item.size

          return item
        })
        if (size > this.downloadMaxSize) {
          message.error(`打包文件失败:打包文件大小超出${formatSize(this.downloadMaxSize)}`)
          return
        }
        let zipArr = [
          {
            size: size,
            fileName: this.zipFileName + '.zip',
            zipDownload: true,
            fileList: selectionRows,
            uid: Date.now() + this.downloadTempIndex++
          }
        ]
        this.dowloadList.push(...zipArr)
      }

      this.$refs.uploadedFile.onClearSelected()
    },
    downloadFinish(id) {
      this.dowloadList = this.dowloadList.filter(item => {
        return !(id == item.id)
      })
    },
    downloadDisabledHandle() {
      return this.disabled || this.downloadDisabled 
    },
    //预览文件 allAuth = 1 不需要权限判断 allAuth用于refs直接调用
    handlePreview(record) {
      
      record.uid = Date.now() + this.downloadTempIndex++
      //下载权限
      let flag = this.downloadDisabledHandle() 
      record[minioUploaderConfig.tokenKey] = minioUploaderConfig.token
      record = Object.assign(record,this.query)
      // var params = qs.stringify(record)
      
      if (this.onPreview) {
        this.onPreview(record)
      } else {
        if (isPreview(record.fileName) && record.size < this.minUploadMaxSize) {
          previewAttach(record).then(result=>{
            const url=URL.createObjectURL(result)
            if (!flag) {
              window.open(process.env.BASE_URL+'/pdf/web/normalViewer.html?file=' + url)
            } else {
              window.open(process.env.BASE_URL+'/pdf/web/viewer.html?file=' + url)
            }
          }).catch(err => {
            this.$message.warning('下载文件失败');
          })
        } else {
          if (!flag ) {
            record.uid = record.uid || Date.now() + this.downloadTempIndex++
            record.zipDownload = false
            record.fileList = []
            this.dowloadList.push(record)
          }
        }
      }
    },
    deleteFile() {
      //附件删除
      let that = this
      let idsArr = this.$refs.uploadedFile.selectedRowKeys
      let selectionRows = this.$refs.uploadedFile.selectionRows
      this.delete(idsArr, selectionRows)
    },
    delete(idsArr, selectionRows,callBack = noop) {
      let that = this
      if (idsArr.length < 1) {
        message.warning('请选择删除的附件')
        return
      }
      let doRemove = () => {
        let params = that.processParams(null)
        params.id = idsArr.join(',')
        Modal.confirm({
          title: '确定删除附件?',
          onOk() {
            deleteAttach(params)
              .then(res => {
                that.fileListDefault = that.fileListDefault.filter(item => {
                  return !idsArr.includes(item.id)
                })
                debugger
                that.$refs.uploadedFile.onClearSelected()
                message.success(`删除附件成功`)
                that.onRemove(idsArr, selectionRows)

                //回调删除文件块
                let deleteParams = params
                let deleteChunksArray = []
                selectionRows.forEach(element => {
                  let params = Object.assign(element, that.query)
                  params.chunkSize = that.finalOptions.chunkSize
                  deleteChunksArray.push(params)
                })
                deleteParams.deleteChunks = deleteChunksArray
                deleteChunks(deleteParams).then(res => {})
                callBack(res)
              })
              .catch(res => {
                message.error(`删除附件失败:${res.message}`)
                callBack(res)
              })
          },
          onCancel() {}
        })
      }

      if (!this.beforeRemove) {
        doRemove()
      } else if (typeof this.beforeRemove === 'function') {
        const before = this.beforeRemove(idsArr, selectionRows)
        if (before && before.then) {
          before.then(() => {
            doRemove()
          }, noop)
        } else if (before !== false) {
          doRemove()
        }
      }
    },
    getFiles() {
      return this.fileListDefault
    },
    clearFiles() {
      let that = this
      idsArr = this.fileListDefault.map(x => {
        return x.id
      })
      selectionRows = that.fileListDefault
      this.delete(idsArr, selectionRows)
    },
    processParams(file = null) {
      //参数处理
      let params = {}
      let query = this.query
      params.chunkSize = this.finalOptions.chunkSize

      if (!!file) {
        params.totalChunks = file.chunks.length
        params.identifier = file.fileMd5
        params.size = file.size
        params.fileName = file.name
        params.fileType = file.fileType
      }

      params = Object.assign(params, query)
      return params
    },

    bindBusKey(params) {
      bindBusKey(params)
        .then(result => {
          message.success(`绑定业务主键成功`)
        })
        .catch(err => {
          essage.error(`绑定业务主键失败：${err.message}`)
        })
    },
    fileSuccess(rootFile, file, message, chunk) {
      let params = this.processParams(file)
    },
    minBeforeUpload(file){
      if(file.size >= this.minUploadMaxSize){
        message.error(`上传${file.name}文件失败:文件大小超出${formatSize(this.minUploadMaxSize)},请使用大文件上传`)
        return false
      }
      if (!!this.beforeUpload) {
       return this.beforeUpload(file)
      }
    },
    fileAdded(file) {
      //单个文件上传监听
      if(file.size < this.minUploadMaxSize){
        message.error(`上传${file.name}文件失败:文件大小低于${formatSize(this.minUploadMaxSize)},请使用普通上传`)
        return false
      }
      //文件大小判断
      if (file.size > this.uploadMaxSize) {
        message.error(`上传${file.name}文件失败:文件大小超出${formatSize(this.uploadMaxSize)}`)
        return false
      }
      file.pause()
      file.isComputeMd5 = true
      this.$emit(kebabCase(FILE_ADDED_EVENT), file)
      if (file.ignored) {
        // is ignored, filter it
        return false
      }
      if (!!this.beforeUpload) {
        const before = this.beforeUpload(file)

        if (before && before.then) {
          before.then(
            processedFile => {
              this.onChange(file, this.fileListDefault)
            },
            () => {
              file.cancel()
              this.freshUploadFiles()
            }
          )
        } else if (before == false) {
          return false
        }
      }
    },

    filesAdded(files, fileList) {
      //上传文件数量超出限定范围
      if (this.limit && this.fileListDefault.length + files.length > this.limit) {
        if (!!this.onExceed) {
          this.onExceed(files, this.fileListDefault)
        } else {
          message.error(
            `当前限制选择 ${this.limit} 个文件，本次选择了 ${files.length} 个文件，共选择了 ${this.fileListDefault
              .length + files.length} 个文件`
          )
        }
        return false
      }
      //文件上传添加到列表监听
      this.$emit(kebabCase(FILES_ADDED_EVENT), files, fileList)

      if (files.ignored || fileList.ignored) {
        // is ignored, filter it
        return false
      }
    },
    fileRemoved(file) {
      //文件上传删除监听
      this.files = this.uploader.files
      this.fileList = this.uploader.fileList
    },
    filesSubmitted(files, fileList) {
      //文件上传提交监听
      this.files = this.uploader.files
      this.fileList = this.uploader.fileList
      if (this.autoUpload) {
        this.uploader.upload()
      }
    },
    freshUploadFiles() {
      this.files = this.uploader.files
      this.fileList = this.uploader.fileList
    },
    allEvent(...args) {
      const name = args[0]
      const EVENTSMAP = {
        [FILE_ADDED_EVENT]: true,
        [FILES_ADDED_EVENT]: true,
        [UPLOAD_START_EVENT]: 'uploadStart'
      }
      const handler = EVENTSMAP[name]
      if (handler) {
        if (handler === true) {
          return
        }
        this[handler].apply(this, args.slice(1))
      }
      args[0] = kebabCase(name)
      this.$emit.apply(this, args)
    },
    init() {
      if (!!this.oldUploadAttachDate) {
        this.oldUploadAttachDate = this.oldUploadAttachDate.replace(/\//g, '-')
      }
      if (!!this.queryParams) {
        this.query = this.queryParams
      } else {
        this.query = {
          dbKey: this.dbKey,
          ftpKey: this.ftpKey,
          busField: this.busField,
          busKey: this.busKey,
          busType: this.busType
        }
      }
    }
  },
  created() {
    this.init()
    this.handleQuery(this.query)
    this.loadData()
    this.options.initialPaused = !this.autoUpload
    let query = this.query
    let finalOptions = Object.assign(this.options, this.finalOptions, { query })
    const uploader = new Uploader(finalOptions)

    this.uploader = uploader
    this.uploader.fileStatusText = this.fileStatusText
    uploader.on('catchAll', this.allEvent)
    uploader.on(FILE_ADDED_EVENT, this.fileAdded)
    uploader.on(FILES_ADDED_EVENT, this.filesAdded)
    uploader.on('fileRemoved', this.fileRemoved)
    uploader.on('filesSubmitted', this.filesSubmitted)
    // uploader.on('fileSuccess', this.fileSuccess)
  },
  mounted() {
    this.$nextTick(res => {
      this.init()
      this.handleQuery(this.query)
    })
  },
  destroyed() {
    const uploader = this.uploader
    uploader.off('catchAll', this.allEvent)
    uploader.off(FILE_ADDED_EVENT, this.fileAdded)
    uploader.off(FILES_ADDED_EVENT, this.filesAdded)
    uploader.off('fileRemoved', this.fileRemoved)
    uploader.off('filesSubmitted', this.filesSubmitted)
    // uploader.off('fileSuccess', this.fileSuccess)
    this.uploader = null
  },
  components: {
    UploaderBtn,
    UploaderDrop,
    UploaderUnsupport,
    UploaderList,
    UploaderFiles,
    UploaderFile,
    UploadedFile,
    DownloadList
  }
}
</script>

<style lang="less" scoped>
@import '../assets/common.less';
</style>
